/*================================================================
*   Copyright (C) 2021 IMVS Ltd. All rights reserved.
*   
*   Filename   ：serial.c
*   Creator    ：Chase Wu
*   Create Date：2021/06/07
*   Description：
*
================================================================*/


#include "serial.h"

#include <stdio.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


static int serial_send_data(serial_t *serial)
{
    int ret = 0;
    int len = sizeof(serial->sendbuf);

    ret = write(serial->fd, serial->sendbuf, len);
    if(ret == len) {
    //    printf("uart send OK!\n");
        return ret;
    } else {
        tcflush(serial->fd, TCOFLUSH);//TCOFLUSH刷新写入的数据但不传送
        return ret;
    }

    return ret;
}

static int serial_receive_data(serial_t *serial)
{
    int ret = 0;

    fd_set fs_read;
    // struct timeval tv_timeout;
 
    FD_ZERO(&fs_read);
    FD_SET(serial->fd, &fs_read);
    // tv_timeout.tv_sec  = (10*20/115200+2);
    // tv_timeout.tv_usec = 0;
 
    do{
        ret = select(serial->fd + 1, &fs_read, NULL, NULL, NULL);
        if (FD_ISSET(serial->fd, &fs_read)) {
            ret = read(serial->fd, serial->recvbuf, sizeof(serial->recvbuf));
            if (ret > 0) {
                for(int i = 0; i < 8; i++){
                    printf("%#x ", serial->recvbuf[i]);
                }
                printf("\n");
                printf("uart receive OK!\n");
            }
            
        }
    }while(serial->mode != SERIAL_LOOPBACK_MODE);

    return ret;
}

static int serial_init(serial_t *serial)
{
    int ret = 0;

    serial->fd = open(serial->dev, O_RDWR | O_NOCTTY | O_NDELAY);
    if (serial->fd < 0) {
       perror("open");
       return -1;
    }
 
    //串口主要设置结构体termios <termios.h>
    struct termios options;
 
    /**1. tcgetattr函数用于获取与终端相关的参数。
    *参数fd为终端的文件描述符，返回的结果保存在termios结构体中
    */
    tcgetattr(serial->fd, &options);
    /**2. 修改所获得的参数*/
    options.c_cflag |= (CLOCAL | CREAD);//设置控制模式状态，本地连接，接收使能
    options.c_cflag &= ~CSIZE;//字符长度，设置数据位之前一定要屏掉这个位
    options.c_cflag &= ~CRTSCTS;//无硬件流控
    options.c_cflag |= CS8;//8位数据长度
    options.c_cflag &= ~CSTOPB;//1位停止位
    options.c_iflag |= IGNPAR;//无奇偶检验位
    options.c_oflag = 0; //输出模式
    options.c_lflag = 0; //不激活终端模式
    cfsetospeed(&options, B115200);//设置波特率
 
    /**3. 设置新属性，TCSANOW：所有改变立即生效*/
    tcflush(serial->fd, TCIFLUSH);//溢出数据可以接收，但不读
    tcsetattr(serial->fd, TCSANOW, &options);

    return ret;
}

static int serial_release(serial_t *serial)
{
    int ret = 0;

    close(serial->fd);

    return ret;
}

static serial_op_t serial_op = {
    .init = serial_init,
    .release = serial_release,

    .send = serial_send_data,
    .receive = serial_receive_data,
};

int serial_destory(serial_t *serial)
{
	return serial->op->release(serial);
}

int serial_create(serial_t **pserial, char *dev)
{
	int ret = 0;

    serial_t* ptmp;
    (*pserial) = (serial_t*)malloc(sizeof(serial_t));
    if(!(*pserial)){
        printf("serial malloc error\n");
        return -1;//-ENOMEM;
    }
    ptmp = *pserial;
    memset(ptmp,0,sizeof(serial_t));
    ptmp->op = &serial_op;
    ptmp->dev = dev;
    ret = ptmp->op->init(ptmp);
    if(ret < 0){
        printf("serial init error\n");
        serial_destory(ptmp);
        return ret;
    }
	return ret;
}

int serial_process(char *dev, int mode)
{
    int ret = 0;

	serial_t *serialtool;

	ret = serial_create(&serialtool, dev);
	if(ret < 0){
		return -1;
	}

    switch(mode){
		case SERIAL_RECV_MODE:{
            serialtool->mode = SERIAL_RECV_MODE;
			ret = serialtool->op->receive(serialtool);
		}break;
		case SERIAL_SEND_MODE:{
            serialtool->mode = SERIAL_SEND_MODE;
			/* prepare data for sending: 1~8 */
			for (int i = 0; i < 8; i++) {
				serialtool->sendbuf[i] = i;
				printf("%#x ", serialtool->sendbuf[i]);
			}
			printf("\n");
			ret = serialtool->op->send(serialtool);
		}break;
        case SERIAL_LOOPBACK_MODE:{
            serialtool->mode = SERIAL_LOOPBACK_MODE;
            for (int i = 0; i < 8; i++) {
				serialtool->sendbuf[i] = i;
				printf("%#x ", serialtool->sendbuf[i]);
			}
			printf("\n");
			ret = serialtool->op->send(serialtool);
            ret = serialtool->op->receive(serialtool);
        }break;
		default:{
			printf("wrong parameter input\n");
		}break;
	}

    ret = serial_destory(serialtool);

    return 0;
}